//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////
//
// Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CEonsHandling::CEonsHandling() {
  InitializeCriticalSection(&m_csRunningCmd);
  m_SupportType = EONS_DISABLED;
  m_dwLines = 0;
  m_dwPnnEntries = 0;
  m_dwOplEntries = 0;
  m_fIsLongName = FALSE;
  m_wSimSw1 = 0;
  m_wSimSw2 = 0;
  m_wCurrentMCC = 0;
  m_wCurrentMNC = 0;
  m_wCurrentLAC = 0;
  m_pRilDevice = 0;
  m_hReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  memset(m_tOplInfo, 0, sizeof(OPL_INFO) * EONS_MAX_OPL);
  memset(m_tPnnInfo, 0, sizeof(PNN_INFO) * EONS_MAX_PNN);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CEonsHandling::~CEonsHandling() {
  Deinit();
  if (m_hReadyEvent) CloseHandle(m_hReadyEvent);
  DeleteCriticalSection(&m_csRunningCmd);
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize and start the EONS handling if available.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
// Return:
//  TRUE or FALSE depending on the success of the operation. FALSE is returned
//  on any problem accessing the module, TRUE is returned if everything was ok
//  even if EONS is not available.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::Init(CRilHandle* pRilDevice) {
DWORD      dwTmp;
BOOL       fRet;

  if (!pRilDevice) {
    return FALSE;
  }

  m_pRilDevice = pRilDevice;

  fRet = FALSE;
  m_SupportType = EONS_DISABLED;
  // Check some input conditions
  if (m_pRilDevice && m_hReadyEvent) {
    // Check registry if EONS is requested
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EonsEnable"), &dwTmp)) {
      if (dwTmp) {
        // Check if EONS is supported by the SIM at all
        if (IsSupported()) {
          // First try to enable module internal EONS handling
          if (EnableModuleHandling()) {
            fRet = TRUE;
            m_fIsLongName = IsLongName();
            m_SupportType = EONS_ENABLED;
          } else if (LoadPnnTable()) {
            // Module doesn't support EONS -> emulate it
            // Load the EONS tables...
            if (LoadOplTable()) {
              // Everthing is perfectly fine
              fRet = TRUE;
              m_SupportType = EONS_EMULATED;
            }
          }
        } else {
          // SIM doesn't support EONS
          fRet = TRUE;
          m_SupportType = EONS_NOT_AVAILABLE;
        }
      }
    } else {
      // No EONS required -> we cannot do anything wrong
      fRet = TRUE;
    }

#ifdef RIL_NOTIFY_EONS
    // Let MMI know that EONS is available
    if ((m_SupportType == EONS_ENABLED) || (m_SupportType == EONS_EMULATED)) {
      m_pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_EONS, NULL, 0);
    }
#endif // RIL_NOTIFY_EONS
  }

  // Send some useful EONS info to debug output if enabled
  TraceEonsInfo();

  //We have to unlock the blocked function RILDrv_GetCurrentOperator() after EONS initialization in any case !!!
  SetEvent(m_hReadyEvent);

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the EONS handling
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::Deinit(void) {
  // Nothin to do for now
  m_pRilDevice = 0;
  return TRUE;
}


//////////////////////////////////////////////////////////////////////////////
//
// Determine the EONS operator information
//
// Params:
//  pHandle     : Instance handle
//  pszLongName : (Out) The long operator name if available or NULL
//  pszShortName: (Out) The short operator name if available or NULL
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::GetOperatorInfo(CRilInstanceHandle* pHandle,
                                    LPCSTR* pszLongName, LPCSTR* pszShortName) {
DWORD  i;
BYTE   pbMCC[3];
BYTE   pbMNC[3];
BOOL   fRet;

  fRet = FALSE;
  *pszLongName = 0;
  *pszShortName = 0;
  // Block call until we finished initializing
  if (WaitForSingleObject(m_hReadyEvent, INFINITE) == WAIT_OBJECT_0) {
    // Check if EONS is emulated or is supported by module
    if (m_SupportType == EONS_ENABLED) {
      // Place the command into the command queue (this may block if the queue is full)
      SYNCBLOCK(m_csRunningCmd);
      if (QueueInternalCmdBlocking(COMMAND_PORT, m_pRilDevice, "AT^SIND=eons,2\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseEONSData, NULL, this)) {
        // Get the found operator name
        if (strlen(m_szModuleLongOperatorName)) {
          *pszLongName = m_szModuleLongOperatorName;
          fRet = TRUE;
        }
        if (strlen(m_szModuleShortOperatorName)) {
          *pszShortName = m_szModuleShortOperatorName;
          fRet = TRUE;
        }
      }

      if (fRet == TRUE) {
        RILRetailTrace((TEXT("RilDrv: EONS module support -> Operator found: \"%a\" - \"%a\"\r\n"), m_szModuleLongOperatorName, m_szModuleShortOperatorName));
      } else {
        RILRetailTrace((TEXT("RilDrv: EONS module support -> Operator not found\r\n")));
      }
    } else if (m_SupportType == EONS_EMULATED) {
      // Place the command into the command queue (this may block if the queue is full)
      SYNCBLOCK(m_csRunningCmd);
      m_dwLines = 0;
      if (QueueInternalCmdBlocking(COMMAND_PORT, m_pRilDevice, "AT^SMONC\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSmoncData, NULL, this)) {
        // Get the BCD codes of MCC and MNC
        pbMCC[0] = m_wCurrentMCC / 100;
        pbMCC[1] = (m_wCurrentMCC - pbMCC[0]) / 10;
        pbMCC[2] = m_wCurrentMCC - pbMCC[0] - pbMCC[1];
        pbMNC[0] = m_wCurrentMNC / 100;
        pbMNC[1] = (m_wCurrentMNC - pbMNC[0]) / 10;
        pbMNC[2] = m_wCurrentMNC - pbMNC[0] - pbMNC[1];

        // Now try to find a corresponding entry in the OPL table
        for (i = 0; i < m_dwOplEntries; i++) {
          if (m_tOplInfo[i].fValid == TRUE) {
            if (((pbMCC[0] == m_tOplInfo[i].pbMCC[0]) || (m_tOplInfo[i].pbMCC[0] == 0x0d)) &&
                ((pbMCC[1] == m_tOplInfo[i].pbMCC[1]) || (m_tOplInfo[i].pbMCC[1] == 0x0d)) &&
                ((pbMCC[2] == m_tOplInfo[i].pbMCC[2]) || (m_tOplInfo[i].pbMCC[2] == 0x0d)) &&
                ((pbMNC[0] == m_tOplInfo[i].pbMNC[0]) || (m_tOplInfo[i].pbMNC[0] == 0x0d)) &&
                ((pbMNC[1] == m_tOplInfo[i].pbMNC[1]) || (m_tOplInfo[i].pbMNC[1] == 0x0d)) &&
                ((pbMNC[2] == m_tOplInfo[i].pbMNC[2]) || (m_tOplInfo[i].pbMNC[2] == 0x0d)) &&
                (m_wCurrentLAC >= m_tOplInfo[i].wLACLow) && (m_wCurrentLAC <= m_tOplInfo[i].wLACHigh)) {
              if (strlen(m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName)) {
                *pszLongName = m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName;
              }
              if (strlen(m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName)) {
                *pszShortName = m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName;
              }
              fRet = TRUE;
              break;
            }
          }
        }
        if (fRet == TRUE) {
          RILRetailTrace((TEXT("RilDrv: EONS emulation -> Operator found: \"%a\" - \"%a\"\r\n"), m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName, m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName));
        } else {
          RILRetailTrace((TEXT("RilDrv: EONS emulation -> Operator not found\r\n")));
        }
      }
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Private implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Check if EONS is supported by the SIM card
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::IsSupported(void) {
BOOL fRet = FALSE;

  // Place the command into the command queue (this may block if the queue is full)
  SYNCBLOCK(m_csRunningCmd);
  m_dwLines = 0;
  if (QueueInternalCmdBlocking(m_pRilDevice->GetFreeAlternativeCmdPort(FALSE), m_pRilDevice, "AT+CRSM=176,28472,0,12,1\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimData, NULL, this)) {
    if ((m_dwLines == 1) && (m_dwBytes[0] == 1) && ((m_bBuffer[0][0] & 0xF0) == 0xF0)) {
      // SIM supports EONS
      fRet = TRUE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Check if EONS module handling returns the long or the short name for the
// provider name types 2 and 3
//
// Return:
//  TRUE if the module returns the loing name, FALSE if the short name is returned
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::IsLongName(void) {
BOOL fRet = FALSE;

  // Place the command into the command queue (this may block if the queue is full)
  SYNCBLOCK(m_csRunningCmd);
  m_dwLines = 0;
  m_wSimSw1 = 0;
  m_wSimSw2 = 0;
  if (QueueInternalCmdBlocking(m_pRilDevice->GetFreeAlternativeCmdPort(FALSE), m_pRilDevice, "AT+CRSM=192,28440,0,0,25\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimData, NULL, this)) {
    if ((m_wSimSw1 == 0x94) && (m_wSimSw2 == 0x04)) {
      // SIM doesn't support short EONS names
      fRet = TRUE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Try to enable module internal EONS handling
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::EnableModuleHandling(void) {
BOOL fRet = FALSE;

  SYNCBLOCK(m_csRunningCmd);
  if (QueueInternalCmdBlocking(COMMAND_PORT, m_pRilDevice, "AT^SIND?\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseEONSQueryData, NULL, this)) {
    // Module supports EONS -> Enable the URC
    if (QueueInternalCmdBlocking(COMMAND_PORT, m_pRilDevice, "AT^SIND=eons,1\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0)) {
      fRet = TRUE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Load the EF-PNN table from the SIM card
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::LoadPnnTable(void) {
DWORD  dwRec;
DWORD  dwCurrRecs;
BOOL   fRet;
char   szCmd[MAX_ATCMD_LEN];

  m_dwPnnEntries = 0;
  memset(m_tPnnInfo, 0, sizeof(PNN_INFO) * EONS_MAX_PNN);
  fRet = TRUE;
  dwRec = 0;
  while (TRUE) {
    dwCurrRecs = GenerateATCmd(szCmd, 0x6fc5, dwRec + 1, 24, EONS_MAX_PNN - dwRec);
    dwRec += dwCurrRecs;

    // Place the command into the command queue (this may block if the queue is full)
    SYNCBLOCK(m_csRunningCmd);
    m_dwLines = 0;
    if (!QueueInternalCmdBlocking(m_pRilDevice->GetFreeAlternativeCmdPort(FALSE), m_pRilDevice, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimData, NULL, this)) {
      fRet = FALSE;
      break;
    }

    // Scan the loaded SIM PNN data and copy it into our internal table
    if (!ScanPnnTable()) {
      fRet = FALSE;
      break;
    }

    // Check if our table is full or there are no more records
    if ((dwRec >= EONS_MAX_PNN) || (m_dwLines < dwCurrRecs)) {
      break;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Load the EF-OPL table from the SIM card
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::LoadOplTable(void) {
DWORD  dwRec;
DWORD  dwCurrRecs;
BOOL   fRet;
char   szCmd[MAX_ATCMD_LEN];

  fRet = FALSE;
  m_dwOplEntries = 0;
  memset(m_tOplInfo, 0, sizeof(OPL_INFO) * EONS_MAX_OPL);
  // Check if the required OPL table has been successfully loaded
  if (m_dwPnnEntries) {
    fRet = TRUE;
    dwRec = 0;
    while (TRUE) {
      dwCurrRecs = GenerateATCmd(szCmd, 0x6fc6, dwRec + 1, 8, EONS_MAX_OPL - dwRec);
      dwRec += dwCurrRecs;

      // Place the command into the command queue (this may block if the queue is full)
      SYNCBLOCK(m_csRunningCmd);
      m_dwLines = 0;
      if (!QueueInternalCmdBlocking(m_pRilDevice->GetFreeAlternativeCmdPort(FALSE), m_pRilDevice, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimData, NULL, this)) {
        fRet = FALSE;
        break;
      }

      // Scan the loaded SIM PNN data and copy it into our internal table
      if (!ScanOplTable()) {
        fRet = FALSE;
        break;
      }

      // Check if our table is full or there are no more records
      if ((dwRec >= EONS_MAX_OPL) || (m_dwLines < dwCurrRecs)) {
        break;
      }
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan the loaded EF-PNN data into our internal table
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ScanPnnTable(void) {
DWORD   i;
DWORD   dwLen;
DWORD   dwBytes;
DWORD   dwBytes2;
BOOL    fRet;

  fRet = TRUE;
  for (i = 0; i < m_dwLines; i++) {
    if (m_dwPnnEntries >= EONS_MAX_PNN) {
      fRet = FALSE;
      break;
    }
    if (m_dwBytes[i] == 24) {
      dwLen = m_bBuffer[i][1];
      dwBytes = (dwLen * 7) / 8;
      if ((dwLen * 7) % 8) dwBytes++;
      if ((dwLen <= EONS_MAX_OPN_LEN) && (dwBytes <= (24 - 3))) {
        if (ScanPnnName(m_bBuffer[i] + 3, m_tPnnInfo[m_dwPnnEntries].szLongOperatorName, dwLen) != TRUE) {
          fRet = FALSE;
        }
        if ((6 + dwBytes) < EONS_MAX_BUF_BYTES) {
          dwLen = m_bBuffer[i][3 + dwBytes + 1];
          dwBytes2 = (dwLen * 7) / 8;
          if ((dwLen * 7) % 8) dwBytes2++;
          if ((dwLen <= EONS_MAX_OPN_LEN) && (dwBytes2 <= (24 - 6 - dwBytes))) {
            if (ScanPnnName(m_bBuffer[i] + 6 + dwBytes, m_tPnnInfo[m_dwPnnEntries].szShortOperatorName, dwLen) != TRUE) {
              fRet = FALSE;
            }
          }
        }
      }
      m_dwPnnEntries++;
    } else {
      fRet = FALSE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan the loaded EF-OPL data into our internal table
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ScanOplTable(void) {
DWORD  i;
BOOL   fRet;

  fRet = TRUE;
  for (i = 0; i < m_dwLines; i++) {
    if (m_dwOplEntries >= EONS_MAX_OPL) {
      fRet = FALSE;
      break;
    }
    if (m_dwBytes[i] == 8) {
      m_tOplInfo[m_dwOplEntries].fValid    = TRUE;
      m_tOplInfo[m_dwOplEntries].pbMCC[0]  = m_bBuffer[i][0] & 0x0f;
      m_tOplInfo[m_dwOplEntries].pbMCC[1]  = m_bBuffer[i][0] >> 4;
      m_tOplInfo[m_dwOplEntries].pbMCC[2]  = m_bBuffer[i][1] & 0x0f;
      m_tOplInfo[m_dwOplEntries].pbMNC[0]  = m_bBuffer[i][2] & 0x0f;
      m_tOplInfo[m_dwOplEntries].pbMNC[1]  = m_bBuffer[i][2] >> 4;
      m_tOplInfo[m_dwOplEntries].pbMNC[2]  = m_bBuffer[i][1] >> 4;
      m_tOplInfo[m_dwOplEntries].wLACLow   = *((WORD *)(&(m_bBuffer[i][3])));
      m_tOplInfo[m_dwOplEntries].wLACHigh  = *((WORD *)(&(m_bBuffer[i][5])));
      m_tOplInfo[m_dwOplEntries].bPLMN     = m_bBuffer[i][7];
      // Check if entry is valid
      if ((m_tOplInfo[m_dwOplEntries].pbMCC[0] > 9) && (m_tOplInfo[m_dwOplEntries].pbMCC[0] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMCC[1] > 9) && (m_tOplInfo[m_dwOplEntries].pbMCC[1] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMCC[2] > 9) && (m_tOplInfo[m_dwOplEntries].pbMCC[2] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMNC[0] > 9) && (m_tOplInfo[m_dwOplEntries].pbMNC[0] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMNC[1] > 9) && (m_tOplInfo[m_dwOplEntries].pbMNC[1] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMNC[2] > 9) && (m_tOplInfo[m_dwOplEntries].pbMNC[2] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if (m_tOplInfo[m_dwOplEntries].wLACLow > m_tOplInfo[m_dwOplEntries].wLACHigh) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if (m_tOplInfo[m_dwOplEntries].bPLMN < 1) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      } else if (m_tOplInfo[m_dwOplEntries].bPLMN >= EONS_MAX_OPL) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      } else {
        m_tOplInfo[m_dwOplEntries].bPLMN--;
        if (!strlen(m_tPnnInfo[m_tOplInfo[m_dwOplEntries].bPLMN].szLongOperatorName)) {
          m_tOplInfo[m_dwOplEntries].fValid = FALSE;
        }
      }
      m_dwOplEntries++;
    } else {
      fRet = FALSE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Generate the linked AT+CRSM command
//
// Params:
//  szBuf     : Buffer for AT command (size has to be MAX_ATCMD_LEN)
//  dwAddr    : Address of SIM file
//  dwStartRec: Record where the AT command list is started
//  dwLength  : Length of records to read
//  dwMaxRecs : Maximal number of records to read
//
// Return:
//  The number of added +CRSM commands
//
//////////////////////////////////////////////////////////////////////////////
DWORD CEonsHandling::GenerateATCmd(char* szBuf, DWORD dwAddr, DWORD dwStartRec, DWORD dwLength, DWORD dwMaxRecs) {
char   szTmpCmd[32];
DWORD  dwRet;
DWORD dwCmdLen;
DWORD dwTmpLen;

  dwRet = 0;
  strcpy(szBuf, "AT");
  dwCmdLen = 2;
  while (TRUE) {
    sprintf(szTmpCmd, "+CRSM=178,%i,%i,4,%i;", dwAddr, dwStartRec, dwLength);
    dwTmpLen = strlen(szTmpCmd);
    if (((dwCmdLen + dwTmpLen) >= (MAX_ATCMD_LEN - 1)) || (dwRet >= dwMaxRecs)) {
      szBuf[dwCmdLen - 1] = '\r';
      szBuf[dwCmdLen] = '\0';
      break;
    }
    strcat(szBuf, szTmpCmd);
    dwCmdLen += dwTmpLen;
    dwStartRec++;
    dwRet++;
  }

  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan the 7 bit operator name from the PNN table and convert it into a
// normal 8 bit string
//
// Params:
//  pSrc : Pointer to source in PNN table
//  pDst : Pointer to destination for transferred string
//  dwLen: Number of characters in operator name
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ScanPnnName(BYTE* pSrc, char* pDst, DWORD dwLen) {
DWORD   i;
DWORD   dwShift;
BYTE    bBuf[EONS_MAX_OPN_LEN + 1];
WCHAR   wszName[EONS_MAX_OPN_LEN + 1];
BOOL    fRet;

  fRet = FALSE;
  memset(bBuf, 0, sizeof(bBuf));
  dwShift = 0;
  for (i = 0; i < dwLen; i++) {
    switch(i % 8) {
      case 0:
        bBuf[i] = pSrc[0 + (dwShift * 7)];
        break;
      case 1:
        bBuf[i] = (pSrc[1 + (dwShift * 7)] << 1) | ((pSrc[0 + (dwShift * 7)] >> 7) & 0x01);
        break;
      case 2:
        bBuf[i] = (pSrc[2 + (dwShift * 7)] << 2) | ((pSrc[1 + (dwShift * 7)] >> 6) & 0x03);
        break;
      case 3:
        bBuf[i] = (pSrc[3 + (dwShift * 7)] << 3) | ((pSrc[2 + (dwShift * 7)] >> 5) & 0x07);
        break;
      case 4:
        bBuf[i] = (pSrc[4 + (dwShift * 7)] << 4) | ((pSrc[3 + (dwShift * 7)] >> 4) & 0x0f);
        break;
      case 5:
        bBuf[i] = (pSrc[5 + (dwShift * 7)] << 5) | ((pSrc[4 + (dwShift * 7)] >> 3) & 0x1f);
        break;
      case 6:
        bBuf[i] = (pSrc[6 + (dwShift * 7)] << 6) | ((pSrc[5 + (dwShift * 7)] >> 2) & 0x3f);
        break;
      case 7:
        bBuf[i] = pSrc[6 + (dwShift * 7)] >> 1;
        dwShift++;
        break;
    }
    bBuf[i] = bBuf[i] & 0x7f;
  }

  if (ParseEncodedString(ENCODING_GSMDEFAULT, (char*)bBuf, (char*)(bBuf + dwLen), wszName, wszName + (EONS_MAX_OPN_LEN + 1))) {
    for (i = 0; i < wcslen(wszName); i++) {
      pDst[i] = (char)wszName[i];
    }
    fRet = TRUE;
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Trace some useful EONS debug info
//
//////////////////////////////////////////////////////////////////////////////
void CEonsHandling::TraceEonsInfo(void) {
#if defined(WANT_RETAIL_RILDRV_OUTPUT) || defined(WANT_FILE_RILDRV_OUTPUT)
  DWORD i;
  switch(m_SupportType) {
    case EONS_DISABLED:
      RILRetailTrace((TEXT("RilDrv: EONS not enabled\r\n")));
      break;
    case EONS_NOT_AVAILABLE:
      RILRetailTrace((TEXT("RilDrv: EONS not available\r\n")));
      break;
    case EONS_ENABLED:
      RILRetailTrace((TEXT("RilDrv: EONS enabled (supported by module)\r\n")));
      break;
    case EONS_EMULATED:
      RILRetailTrace((TEXT("RilDrv: EONS enabled (not supported by module -> emulated)\r\n")));
      RILRetailTrace((TEXT("        -----------------------------------------------------\r\n")));
      RILRetailTrace((TEXT("        MCC  MNC  LACLO  LACHI  Names (Long <-> Short)\r\n")));
      RILRetailTrace((TEXT("        -----------------------------------------------------\r\n")));
      for (i = 0; i < m_dwOplEntries; i++) {
        if (m_tOplInfo[i].fValid != TRUE) {
          RILRetailTrace((TEXT("        Entry invalid!\r\n")));
        } else {
          RILRetailTrace((TEXT("        %x%x%x  %x%x%x  %5d  %5d  \"%a\" <-> \"%a\"\r\n"),
                      m_tOplInfo[i].pbMCC[0], m_tOplInfo[i].pbMCC[1], m_tOplInfo[i].pbMCC[2],
                      m_tOplInfo[i].pbMNC[0], m_tOplInfo[i].pbMNC[1], m_tOplInfo[i].pbMNC[2],
                      m_tOplInfo[i].wLACLow, m_tOplInfo[i].wLACHigh,
                      m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName,
                      m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName));
        }
      }
      RILRetailTrace((TEXT("        -----------------------------------------------------\r\n")));
      break;
    default:
      RILRetailTrace((TEXT("RilDrv: Internal EONS error\r\n")));
      break;
  }
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned AT^SIND? data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseEONSQueryData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling* pInstance = (CEonsHandling*)pParam;

  pBlob = NULL;
  cbBlob = 0;

  if (!pInstance) {
    return E_FAIL;
  }

  // Parse "<prefix>^SIND: eons,"
  if (ParseRspPrefix(szRsp, szRsp)                       &&
      MatchStringAnywhere(szRsp, "^SIND: eons,", szRsp)  &&
      MatchStringEnd(szRsp, "\r\nOK\r\n", szRsp)) {
    return S_OK;
  }

  return E_FAIL;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned AT^SIND=eons,2 data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseEONSData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling*  pInstance = (CEonsHandling*)pParam;
UINT            nType, nMode;
WCHAR           wszEonsOperator[EONS_MAX_OPN_LEN + 1];
WCHAR           wszServiceProvider[EONS_MAX_OPN_LEN + 1];
DWORD           i;
HRESULT         Ret = E_FAIL;

  pBlob = NULL;
  cbBlob = 0;
  Ret = E_FAIL;

  if (!pInstance) {
    return E_FAIL;
  }

  memset(pInstance->m_szModuleLongOperatorName, 0, sizeof(pInstance->m_szModuleLongOperatorName));
  memset(pInstance->m_szModuleShortOperatorName, 0, sizeof(pInstance->m_szModuleShortOperatorName));

  // Parse "^SIND: eons, <Mode>,<Type>,<"Eons-Operator-Name">,<"Service-Provider-Name">"
  // "Service-Provider-Name" is delivered by the module but currently not needed for eons support
  if (ParseRspPrefix(szRsp, szRsp)                        &&
      MatchStringBeginning(szRsp, "^SIND: eons,", szRsp)  &&
      ParseUInt(szRsp, TRUE, nMode, szRsp)                &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseUInt(szRsp, TRUE, nType, szRsp)                &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszEonsOperator, wszEonsOperator + (EONS_MAX_OPN_LEN + 1)) &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszServiceProvider, wszServiceProvider + (EONS_MAX_OPN_LEN + 1)) &&
      ParseRspPrefix(szRsp, szRsp)                        &&
      MatchStringBeginning(szRsp, "OK", szRsp)            &&
      ParseRspPostfix(szRsp, szRsp)) {
    // Convert the found unicode string
    if ((nType == 1) || (nType == 4)) {
      for (i = 0; i < wcslen(wszEonsOperator); i++) {
        pInstance->m_szModuleLongOperatorName[i] = (char)wszEonsOperator[i];
      }
      Ret = S_OK;
    } else if ((nType == 2) || (nType == 3)) {
      if (pInstance->m_fIsLongName == TRUE) {
        for (i = 0; i < wcslen(wszEonsOperator); i++) {
          pInstance->m_szModuleLongOperatorName[i] = (char)wszEonsOperator[i];
        }
      } else {
        for (i = 0; i < wcslen(wszEonsOperator); i++) {
          pInstance->m_szModuleShortOperatorName[i] = (char)wszEonsOperator[i];
        }
      }
      Ret = S_OK;
    }
  }

  return Ret;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned SIM data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseSimData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling*  pInstance = (CEonsHandling*)pParam;
UINT            i;
UINT            nSW1;
UINT            nSW2;
LPCSTR          szResponseString;
UINT            cbSize;
BYTE*           pbResponseWalk;
LPCSTR          pchResponseStringWalk;
HRESULT         Ret;

  pBlob = NULL;
  cbBlob = 0;

  if (!pInstance) {
    return E_FAIL;
  }

  pInstance->m_dwLines = 0;

  Ret = S_OK;
  while (TRUE) {
    // Parse "<prefix>+CRSM: <sw1>,<sw2>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "+CRSM: ", szRsp)  ||
        !ParseUInt(szRsp, TRUE, nSW1, szRsp)            ||
        !ParseRspParamSeparator(szRsp, szRsp)           ||
        !ParseUInt(szRsp, TRUE, nSW2, szRsp)) {
      break;
    }

    // Store the SIM response
    pInstance->m_wSimSw1 = nSW1;
    pInstance->m_wSimSw2 = nSW2;

    // Check the return values
    if ((nSW1 != 144) || (nSW2 != 0)) {
      if (!FindRspPostfix(szRsp, szRsp)) {
        Ret = E_FAIL;
      }
      break;
    }

    // Parse ","
    if (!ParseRspParamSeparator(szRsp, szRsp)) {
      // No response data present
      if (!FindRspPostfix(szRsp, szRsp)) {
        Ret = E_FAIL;
      }
      break;
    }

    // Parse "<postfix>" and remember start of SIM data
    szResponseString = szRsp;
    if (!FindRspPostfix(szRsp, szRsp)) {
      Ret = E_FAIL;
      break;
    }

    // Calculate size of SIM data
    cbSize = ((szRsp - szResponseString) - 2) / 2;

    // Check the data size
    if ((cbSize > EONS_MAX_BUF_BYTES) || (cbSize == 0)) {
      Ret = E_FAIL;
      break;
    }

    // Decode the response data
    pbResponseWalk = pInstance->m_bBuffer[pInstance->m_dwLines];
    pchResponseStringWalk = szResponseString;
    for (i = 0; i < cbSize; i++) {
      *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
      pbResponseWalk++;
      pchResponseStringWalk += 2;
    }

    pInstance->m_dwBytes[pInstance->m_dwLines] = cbSize;
    pInstance->m_dwLines++;
    if (pInstance->m_dwLines >= EONS_MAX_BUF_LINES) {
      Ret = E_FAIL;
      break;
    }
  }

  return Ret;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned SMONC data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseSmoncData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling*  pInstance = (CEonsHandling*)pParam;
UINT            nMCC;
UINT            nMNC;
UINT            nLAC;

  pBlob = NULL;
  cbBlob = 0;

  if (!pInstance) {
    return E_FAIL;
  }

  pInstance->m_wCurrentMCC = 0;
  pInstance->m_wCurrentMNC = 0;
  pInstance->m_wCurrentLAC = 0;

  // Parse "<prefix>^SMONC: <MCC>,<MNC>,<LAC>"
  if (ParseRspPrefix(szRsp, szRsp)                    &&
      MatchStringBeginning(szRsp, "^SMONC: ", szRsp)  &&
      ParseUInt(szRsp, TRUE, nMCC, szRsp)             &&
      ParseRspParamSeparator(szRsp, szRsp)            &&
      ParseUInt(szRsp, TRUE, nMNC, szRsp)             &&
      ParseRspParamSeparator(szRsp, szRsp)            &&
      ParseUInt(szRsp, TRUE, nLAC, szRsp)) {
    pInstance->m_wCurrentMCC = nMCC;
    pInstance->m_wCurrentMNC = nMNC;
    pInstance->m_wCurrentLAC = nLAC;
    return S_OK;
  }

  return E_FAIL;
}

